#ifndef ADM_MATRIX_H_
#define ADM_MATRIX_H_
#include <iostream>
typedef unsigned int uint;
namespace Andromeda {
  class vec2 {
  public : 
    vec2(float v0 =0.0, float v1 = 0.0) {
      data_[0] = v0;
      data_[1] = v1;
    }
    ~vec2() {

    }
    vec2 (const vec2& rhs) {
      for (int i=0; i<2; i++ )
        data_[i] = rhs.data_[i];
    }
    vec2& operator = (const vec2& rhs ) {
      for (int i=0; i<2; i++ )
        data_[i] = rhs.data_[i];
      return (*this);
    }
    float& operator [](int index){
      return data_[index]; //boundary index out of range
    }
    float operator [](int index) const{
      return data_[index]; //boundary index out of range
    }
   protected :
      float data_[2];
  };
  class vec3 {
  public:
    vec3() {
      for (int i=0; i<3; i++)
        data_[i] = 0.0;
    }
    ~vec3() {

    }
    vec3(float v0_, float v1_, float v2_) {
      data_[0] = v0_;
      data_[1] = v1_;
      data_[2] = v2_;
    }
    vec3 (const vec3& rhs) {
      for (int i=0; i<3; i++ )
        data_[i] = rhs.data_[i];
    }
    vec3& operator = (const vec3& rhs ) {
      for (int i=0; i<3; i++ )
        data_[i] = rhs.data_[i];
      return (*this);
    }
    float& operator [](int index){
      return data_[index]; //boundary index out of range
    }
    float operator [](int index) const{
      return data_[index]; //boundary index out of range
    }


    float operator *(const vec3& rhs) {
      float res;
      res= 0.0;
      for (int i=0; i<3; i++) {
       
        res += rhs.data_[i]*data_[i];
      }
      return res;
    }
   
  protected :
    float data_[3];
  };

  class vec4 {
  public:
    vec4() {
      for (int i=0; i<4; i++)
        data_[i] = 0.0;
    }
    vec4 (const vec4& rhs) {
      for (int i=0; i< 4; i++)
        data_[i] = rhs.data_[i];
    }
    vec4& operator = (const vec4& rhs) {
      for (int i=0; i < 4; i++)
        data_[i] = rhs.data_[i];
      return (*this);
    }
    vec4(float v0_, float v1_, float v2_, float v3_) {
      data_[0] = v0_;
      data_[1] = v1_;
      data_[2] = v2_;
      data_[3] = v3_;
    }
    float& operator [](int index){
      return data_[index];
    }
    float operator [](int index) const{
      return data_[index];
    }
    float operator *(const vec4& rhs) {
      float res;
      res= 0.0;
      for (int i=0; i<4; i++) {
        res += rhs.data_[i]*data_[i];
      }
      return res;
    }
    ~vec4 () {

    }
  protected :
    float data_[4];
  };
 
  class mat2;
  class mat3 {
  public :
    mat3() { //all zero matrix
      for (int i=0; i< 3; i++) {
        for (int j=0; j<3; j++) {
          data_[i][j] = 0.0;
        }
      }     
    }
    mat3(const mat3& rhs) {
      for (int i=0; i<3; i++)
          data_[i] = rhs.data_[i];
    }
    mat3& operator = (const mat3& rhs) {
      for (int i=0; i<3; i++)
          data_[i] = rhs.data_[i];     

      return (*this);
    }
    mat3(float init_diag) {
      for (int i=0; i< 3; i++) {
        for (int j=0; j<3; j++) {
          if (i == j)
            data_[i][j] = init_diag;
          else
            data_[i][j] = 0.0;
        }
      }
    }
    //overload function *
    mat3 operator *(const mat3& rhs) {
      mat3 res;
      float sum;
      for (int i=0; i<3; i++ ) {
        for (int j=0; j<3; j++) {
          sum = 0.0;
          vec3 rol = rhs.data_[j];
          for (int k =0; k<3; k++)
             sum += data_[k][i] * rol[k];
          res[i][j] = sum;
        }
      }
      return res;
    }
    vec3 operator *(const vec3& rhs) {
       vec3 res;
       float sum;
       for (int i =0; i< 3; i++) {
          sum = 0.0;
          for (int j=0; j<3; j++) {
            sum += data_[j][i] * rhs[j];
          }
          res[i] = sum;
       }
       return res;
    }
 
    //overload function [] to access col_th vector
    vec3& operator [](const int col) {
      return data_[col]; 
    }
    vec3 operator [](const int col) const{
      return data_[col]; 
    }


    ~mat3(){

    }
  protected :
    vec3 data_[3]; //column vector

  };

  class mat4 {
  public :
    mat4() { //all zero matrix
      for (int i=0; i< 4; i++) {
        for (int j=0; j<4; j++) {
          data_[i][j] = 0.0;
        }
      }     
    }
    mat4(float init_diag) {
      for (int i=0; i< 4; i++) {
        for (int j=0; j<4; j++) {
          if (i == j)
            data_[i][j] = init_diag;
          else
            data_[i][j] = 0.0;
        }
      }
    }

    mat4(const mat4& rhs) {
      for (int i=0; i<4; i++)
          data_[i] = rhs.data_[i];
    }
    mat4& operator = (const mat4& rhs) {
      for (int i=0; i<4; i++)
          data_[i] = rhs.data_[i];     

      return (*this);
    }
    //overload function *
    mat4 operator *(const mat4& rhs) {
      mat4 res;
      float sum;
      for (int i=0; i<4; i++ ) {
        for (int j=0; j<4; j++) {
          sum = 0.0;
          vec4 col = rhs.data_[j];
          for (int k =0; k<4; k++)
             sum += data_[k][i] * col[k];
          res[i][j] = sum;
        }
      }
      return res;
    }   
    vec4 operator *(const vec4& rhs) {
       vec4 res;
       float sum;
       for (int i =0; i< 4; i++) {
          sum = 0.0;
          for (int j=0; j<3; j++) {
            sum += data_[j][i] * rhs[j];
          }
          res[i] = sum;
       }
       return res;
    }


    //overload function [] to access col_th vector
    vec4& operator [](const int col) { //for [] at the left side
      return data_[col]; 
    }
    vec4 operator [](const int col) const{ //for [] at the right side
      return data_[col]; 
    }
    ~mat4(){

    }
  protected :
    vec4 data_[4]; //four vec4
  };


}


#endif
